#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import division
import threading
import SocketServer
import time
import socket
import logging
import traceback
import binascii
import struct
from Crc16 import Crc16
import Config
import datetime

"""
数据处理流程
tcp线程接收注册包和心跳包（该功能已实现）
线程中进行计时，当多次收到心跳包时间超过250s后，发送读取数据指令（该功能已实现）
（发送前根据心跳包中设备地址查询数据库，找到对应设备，根据设备信息发送对应的数据读取指令，如果查询不到设备，则认为设备未绑定，不做后续处理）
tcp线程接收数据，对数据进行解析（有些设备需要发送两次指令，读取不同的数据）（该功能已实现），解析后将数据保存到数据库
"""

logging.basicConfig(level=logging.DEBUG,
                    filename='debug.log',
                    filemode='a',
                    format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s')

# 使用Struct去解析二进制数据更方便（百度搜索Struct的用法），以前使用hex转成十六进制去解不是一个很好的方法。
# 注册和心跳二进制数据解析结构
reg_heart_struct = struct.Struct('>BBBB16sBB6s2s')

data_struct = struct.Struct('>BBB6sddffff2s2s2s')

modbus20_send_struct = struct.Struct('!8B')
ljs_send_struct = struct.Struct('!6B')
tx13_send_struct = struct.Struct('!20B')
evc102_send_struct = struct.Struct('!8B')

data_md12_struct = struct.Struct(">BBB8B8B4B4B4B4B2B")
data_md22_struct = struct.Struct(">BBBddffff2s")
data_md31_struct = struct.Struct('>BBBddhffddffd2s2s6s2s')
data_md31_ctrl_struct = struct.Struct('>BBB2s8s6s2slll2sqq2s')

data_md32_struct = struct.Struct('>BBBddhffddffd2s2s6sd8shdf6s6s12sh10shhh52s2s')
data_ljs_struct = struct.Struct('>7B4B4B4B4B6BB')

data_tx13_struct = struct.Struct('>5B7B4B6B4B4B2BB2BB')

data_evc102_struct = struct.Struct('>3Bdfffff2B')


class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):
    """
    请求处理handler
    """

    def handle(self):
        datetime_last = datetime.datetime.now()
        timer_sum = 0
        first_send_flag = True # 先发一条数据
        print("ddd")
        try:
            while True:
                datatime_now = datetime.datetime.now()
                datatime_datla = datatime_now - datetime_last
                timer_sum = timer_sum + datatime_datla.seconds
                print datatime_datla
                print "time:%d" % timer_sum

                data = self.request.recv(Config.BUFFER_SIZE)
                logging.debug("{} send: {}".format(self.client_address, binascii.hexlify(data)))
                print binascii.hexlify(data)
                # 解析二进制数据
                print "len:%d" % len(data)
                if data:
                    datetime_last = datatime_now
                    # 注意 可能会收到故障数据包，表明流量计异常 1783022135 五个字节
                    if len(data) == 5:
                        # 收到故障数据表，试图发送请求恢复数据
                        # 发送时间控制
                        if timer_sum > 250:
                            # todo  发数据前根据地址从数据库获取流量计类型，
                            #  再根据具体的类型发送下发指令
                            print "send data"
                            # EVC102
                            flowmeter_type = "EVC102"
                            # 该方法下发读取流量计的数据
                            self.send_flowmeter_request(flowmeter_type)
                            timer_sum = 0  # 时间统计清零
                    elif len(data) == 30:
                        # 注册包
                        unpacked = reg_heart_struct.unpack(data)
                        logging.debug("reg unpack data: {}".format(unpacked))
                        print "heart unpacked:{}".format(unpacked)
                        # 当前为ljs数据，ljs数据解析，ljs数据和心跳包一样也是30个字节
                        print unpacked[0]
                        if unpacked[0] == 85:
                            unpacked = data_ljs_struct.unpack(data)
                            print unpacked
                            flowmeter_address = unpacked[2]
                            flowmeter_g = self.cal_ljs_data(unpacked[7:11])
                            flowmeter_b = self.cal_ljs_data(unpacked[11:15])
                            flowmeter_t = self.cal_ljs_data(unpacked[15:19])
                            flowmeter_p = self.cal_ljs_data(unpacked[19:23])
                            flowmeter_total = self.cal_ljs_data(unpacked[23:29])
                            print "累计%f, 工况%f, 标况%f,温度%f, 压力%f" % (
                                flowmeter_total, flowmeter_g, flowmeter_b, flowmeter_t,
                                flowmeter_p)
                            # todo 保存数据到数据库
                        else:
                            device_id = unpacked[4]  # 设备唯一id 不是流量计id
                            # todo 根据设备id查询到对应的集中器及下属的流量计

                        # 发送时间控制 要求五分钟更新一次数据  提前50s发送指令
                        if first_send_flag or timer_sum > 250:
                            first_send_flag = False
                            # todo  发数据前根据地址从数据库获取流量计类型，
                            #  再根据具体的类型发送下发指令
                            print "send data"
                            # modbus3.1 读取卡控（不用，测试用）
                            # send_request_list1 = [0x17, 0x03, 0x01, 0x2c, 0x00, 0x18]
                            # crc16 = Crc16.createcrc1(send_request_list1)
                            # send_request_list1.append(crc16[0])
                            # send_request_list1.append(crc16[1])
                            # print send_request_list1
                            # send_bytes = modbus20_send_struct.pack(*send_request_list1)
                            # self.request.sendall(send_bytes)
                            # timer_sum = 0

                            # modbus3.2 流量计数据(不用，测试用)
                            # send_request_list1 = [0x17, 0x03, 0x00, 0x0a, 0x00, 0x15]
                            # crc16 = Crc16.createcrc1(send_request_list1)
                            # send_request_list1.append(crc16[0])
                            # send_request_list1.append(crc16[1])
                            # print send_request_list1
                            # send_bytes = modbus20_send_struct.pack(*send_request_list1)
                            # self.request.sendall(send_bytes)
                            # timer_sum = 0
                            # EVC102 实例（flowmeter_type类型应从数据库获取）
                            flowmeter_type = "modbus32"
                            # 该方法下发读取流量计的数据
                            self.send_flowmeter_request(flowmeter_type)
                            timer_sum = 0 # 时间统计清零

                    # 天信13数据
                    elif len(data) == 36:
                        unpacked = data_tx13_struct.unpack(data)
                        print unpacked
                        flowmeter_address = unpacked[1]
                        flowmeter_b = self.cal_ljs_data(unpacked[12:16])
                        flowmeter_g = flowmeter_b
                        # 总量为2字节BCD + 4字节浮点数
                        flowmeter_total_bcd = int(binascii.hexlify(data[16: 18]))*10**6
                        flowmeter_total = flowmeter_total_bcd + self.cal_ljs_data(unpacked[18:22])
                        flowmeter_t = self.cal_ljs_data(unpacked[22:26])
                        flowmeter_p = self.cal_ljs_data(unpacked[26:30])
                        print "累计%f, 工况%f, 标况%f,温度%f, 压力%f" % (
                            flowmeter_total, flowmeter_g, flowmeter_b, flowmeter_t,
                            flowmeter_p)
                        # todo 保存数据到数据库
                    # evc102数据
                    elif len(data) == 33:
                        if flowmeter_type == "EVC102":
                            unpacked = data_evc102_struct.unpack(data)
                            print unpacked
                            flowmeter_address = unpacked[0]
                            flowmeter_total_b = unpacked[3]
                            flowmeter_total_g = unpacked[8]
                            flowmeter_g = unpacked[5]
                            flowmeter_b = unpacked[4]
                            flowmeter_t = unpacked[6]
                            flowmeter_p = unpacked[7]
                            print "工况累计%f, 标况累计%f, 工况%f, 标况%f,温度%f, 压力%f" % (
                                flowmeter_total_g, flowmeter_total_b, flowmeter_g, flowmeter_b, flowmeter_t,
                                flowmeter_p)
                            # todo 保存数据到数据库
                    # modbus1.2 数据
                    elif len(data) == 37:
                        if flowmeter_type == "modbus12":
                            unpacked = data_md12_struct.unpack(data)
                            print unpacked
                            flowmeter_address = unpacked[0]
                            flowmeter_total_g = self.cal_modbus12_data(unpacked[3:11])
                            flowmeter_total_b = self.cal_modbus12_data(unpacked[11:19])
                            flowmeter_g = self.cal_modbus12_data(unpacked[19:23])
                            flowmeter_b = self.cal_modbus12_data(unpacked[23:27])
                            flowmeter_t = self.cal_modbus12_data(unpacked[27:31])
                            flowmeter_p = self.cal_modbus12_data(unpacked[31:35])
                            print "工况累计%f, 标况累计%f, 工况%f, 标况%f,温度%f, 压力%f" % (
                                flowmeter_total_g, flowmeter_total_b, flowmeter_g, flowmeter_b, flowmeter_t,
                                flowmeter_p)
                            # todo 保存数据到数据库
                        # 暂时没测试
                        elif flowmeter_type == "modbus22":
                            unpacked = data_md22_struct.unpack(data)
                            print unpacked
                    # modbus3.1 控制数据（不使用，测试用）
                    elif len(data) == 53:
                        unpacked = data_md31_ctrl_struct.unpack(data)
                        print unpacked

                    # modbus2.0协议
                    elif len(data) == 47:
                        unpacked = data_struct.unpack(data)
                        logging.debug("data unpack data:{}".format(unpacked))
                        flowmeter_address = unpacked[0]
                        time1 = [str(hex(ord(i)).replace('0x', '')) for i in unpacked[3]]
                        # 工况累计
                        flowmeter_total_g = unpacked[4]
                        # 标况累计
                        flowmeter_total_b = unpacked[5]
                        # 工况流量
                        flowmeter_g = unpacked[6]
                        # 标况流量
                        flowmeter_b = unpacked[7]
                        # 温度
                        flowmeter_t = unpacked[8]
                        # 压力
                        flowmeter_p = unpacked[9]
                        print time1
                        print unpacked
                        print "工况累计%f, 标况累计%f, 温度%f, 压力%f" % (
                            flowmeter_total_g, flowmeter_total_b, flowmeter_t, flowmeter_p)

                        # todo 保存数据到数据库
                    # modbus3.1数据
                    elif len(data) == 73:
                        unpacked = data_md31_struct.unpack(data)
                        print unpacked
                        flowmeter_address = unpacked[0]
                        flowmeter_rest_money = unpacked[3]  # 剩余金额
                        flowmeter_all_use = unpacked[4]  # 表内累计用气量
                        flowmeter_price = unpacked[5]  # 表内当前价格
                        flowmeter_b = unpacked[6]  # 标况流量
                        flowmeter_g = unpacked[7]  # 工况流量
                        flowmeter_total_b = unpacked[8]  # 标况累计流量
                        flowmeter_total_g = unpacked[9]  # 工况累计流量
                        flowmeter_t = unpacked[10]  # 计量温度
                        flowmeter_p = unpacked[11]  # 计量压力

                        print "工况累计%f, 标况累计%f, 温度%f, 压力%f" % (
                            flowmeter_total_g, flowmeter_total_b, flowmeter_t, flowmeter_p)
                        print "剩余金额%f, 表内累计用气量%f, 表内当前价格%d分" % (
                            flowmeter_rest_money, flowmeter_all_use, flowmeter_price)
                        # todo 保存数据到数据库
                    # modbus3.2数据
                    elif len(data) == 197:
                        unpacked = data_md32_struct.unpack(data)
                        print unpacked
                        flowmeter_address = unpacked[0]
                        flowmeter_rest_money = unpacked[3]  # 剩余金额
                        flowmeter_all_use = unpacked[4]  # 表内累计用气量
                        flowmeter_price = unpacked[5]  # 表内当前价格
                        flowmeter_b = unpacked[6]  # 标况流量
                        flowmeter_g = unpacked[7]  # 工况流量
                        flowmeter_total_b = unpacked[8]  # 标况累计流量
                        flowmeter_total_g = unpacked[9]  # 工况累计流量
                        flowmeter_t = unpacked[10]  # 计量温度
                        flowmeter_p = unpacked[11]  # 计量压力
                        flowmeter_price_unit = unpacked[-4]  # 价格单位
                        flowmeter_door_status = unpacked[-3]  # 阀门状态
                        print "工况累计%f, 标况累计%f, 温度%f, 压力%f" % (
                            flowmeter_total_g, flowmeter_total_b, flowmeter_t, flowmeter_p)
                        print "剩余金额%f, 表内累计用气量%f, 表内当前价格%d分" % (
                            flowmeter_rest_money, flowmeter_all_use, flowmeter_price)
                        print "价格单位%d, 阀门状态%d" % (
                            flowmeter_price_unit, flowmeter_door_status)
                        # todo 保存数据到数据库
                else:
                    logging.debug("connection lost")
                    break

        except:
            traceback.print_exc()
            logging.debug("{} 连接断开".format(self.client_address))
        finally:
            self.request.close()

    def send_flowmeter_request(self, flowmeter_type):
        """
        下发读取流量计数据的指令
        modbus22  modbus13还没测试  不具备测试条件
        :param flowmeter_type: 流量计类型 应从数据库获取
        :return: 无
        """
        if flowmeter_type == "EVC102":
            # evc102 流量计数据
            send_request_list1 = [0x17, 0x03, 0x00, 0x00, 0x00, 0x0e]
            crc16 = Crc16.createcrc1(send_request_list1)
            send_request_list1.append(crc16[0])
            send_request_list1.append(crc16[1])
            send_bytes = evc102_send_struct.pack(*send_request_list1)
        elif flowmeter_type == "tx13":
            # 天信V13流量计数据
            send_request_list1 = [0xcc, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0xEE]
            send_bytes = tx13_send_struct.pack(*send_request_list1)
        elif flowmeter_type == "ljs":
            # ljs流量计数据
            send_request_list1 = [0x55, 0x55, 0x17, 0x02, 0x00, 0x19]
            send_bytes = ljs_send_struct.pack(*send_request_list1)
        elif flowmeter_type == "modbus12":
            # modbus1.2 流量计数据
            send_request_list1 = [0x17, 0x03, 0x00, 0x00, 0x00, 0x10]
            crc16 = Crc16.createcrc1(send_request_list1)
            send_request_list1.append(crc16[0])
            send_request_list1.append(crc16[1])
            send_bytes = modbus20_send_struct.pack(*send_request_list1)
        elif flowmeter_type == "modbus20":
            # modbus2.0
            send_request_list = [0x17, 0x03, 0x00, 0x0a, 0x00, 0x15]
            crc16 = Crc16.createcrc1(send_request_list)
            send_request_list.append(crc16[0])
            send_request_list.append(crc16[1])
            send_bytes = modbus20_send_struct.pack(*send_request_list)
        elif flowmeter_type == "modbus31":
            # modbus3.1
            send_request_list1 = [0x17, 0x03, 0x02, 0x50, 0x00, 0x22]
            crc16 = Crc16.createcrc1(send_request_list1)
            send_request_list1.append(crc16[0])
            send_request_list1.append(crc16[1])
            send_bytes = modbus20_send_struct.pack(*send_request_list1)
        elif flowmeter_type == "modbus32":
            # modbus3.2 金额数据+流量计数据
            send_request_list1 = [0x17, 0x03, 0x03, 0x50, 0x00, 0x60]
            crc16 = Crc16.createcrc1(send_request_list1)
            send_request_list1.append(crc16[0])
            send_request_list1.append(crc16[1])
            send_bytes = modbus20_send_struct.pack(*send_request_list1)
        elif flowmeter_type == "modbus22":
            # modbus2.2 流量计数据(还没测试，不具备测试条件)
            flowmeter_type = "modbus22"
            send_request_list1 = [0x17, 0x03, 0x00, 0x00, 0x00, 0x10]
            crc16 = Crc16.createcrc1(send_request_list1)
            send_request_list1.append(crc16[0])
            send_request_list1.append(crc16[1])
            send_bytes = modbus20_send_struct.pack(*send_request_list1)

        self.request.sendall(send_bytes)

    @staticmethod
    def cal_ljs_data(data_list):
        """
        ljs和天信都用该算法计算  天信协议还需要把bcd数据先转换成16进制数据
        :param data_list:
        :return:
        """
        print data_list
        # 负数的处理
        if data_list[0] > 128:
            if len(data_list) == 4:
                temp_data = data_list[1] + data_list[2] / 256 + data_list[3] / 65536
            else:
                temp_data = data_list[1] + data_list[2] / 256 + data_list[3] / 65536 + data_list[4] / 65536 / 256 + \
                            data_list[5] / 65536 / 65536
            while temp_data > 1:
                temp_data = temp_data / 2
            result = 2 ** (data_list[0] - 128) * temp_data
            result = result * -1
        else:
            # 获取尾数
            if len(data_list) == 4:
                temp_data = data_list[1] + data_list[2] / 256 + data_list[3] / 65536
            else:
                temp_data = data_list[1] + data_list[2] / 256 + data_list[3] / 65536 + data_list[4] / 65536 / 256 + \
                            data_list[5] / 65536 / 65536
            while temp_data > 1:
                temp_data = temp_data / 2
            result = 2 ** data_list[0] * temp_data
        return result

    @staticmethod
    def cal_modbus12_data(data_list):
        """
        modbus12协议数据解包方法
        :param data_list:
        :return:
        """
        print data_list
        if len(data_list) == 8:
            result = data_list[0] * 4294967296 + data_list[1] * 16777216 + data_list[3] * 65536 + data_list[4] * 256 + \
                     data_list[5]
            result = result + (data_list[6] * 256 + data_list[7]) / 65536
        else:
            result = data_list[0] * 65536 + data_list[1] * 256 + data_list[2]
            result = result + data_list[3] / 256
        return result

    def setup(self):
        logging.debug("before handle,连接建立：{}".format(self.client_address))

    def finish(self):
        logging.debug("finish run after handle")


class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    """
    多线程处理的TCP服务器
    """
    # 允许重用地址
    allow_reuse_address = True


if __name__ == "__main__":
    server = ThreadedTCPServer((Config.SERVER_HOST, Config.SERVER_PORT3), ThreadedTCPRequestHandler)
    server_rev_thread = threading.Thread(target=server.serve_forever)

    server_rev_thread.start()
    print('Bind TCP on %s...' % Config.SERVER_PORT3)
